<template>
    <div class="tetris">
        <div class="frame">
            <div class="top">
                <yk-space justify="center">
                    <p>历史最高 {{gameStatus.blockSum}}</p>
                </yk-space>
            </div>
            <yk-space size="0" align="stretch">
                <yk-space class="side" dir="vertical" justify="between" align="center">
                    <yk-space dir="vertical" size="30px">
                        <yk-space dir="vertical" size="s" align="center">
                            <p>得分</p>
                            <div class="lines">{{gameStatus.score}}</div>
                        </yk-space>
                        <yk-space dir="vertical" size="s" align="center">
                            <p>消除行</p>
                            <div class="lines">{{gameStatus.blockSum}}</div>
                        </yk-space>
                    </yk-space>
                    <div class="switch">
                        <IconRefreshOutline class="icon" @click="restartGame" />
                    </div>
                </yk-space>
                <div class="frame-primary">
                    <div class="row" size="ss" v-for="(row,i) in gameFrame.primary" :key="i">
                        <div class="col" v-for="(col,j) in row" :key="j">
                            <div class="square" :style="{background:col.bg}"></div>
                        </div>
                    </div>
                </div>
                <yk-space class="side" dir="vertical" justify="between" align="center">
                    <div class="frame-secondary">
                        <div class="row" v-for="(row,i) in gameFrame.secondary" :key="i">
                            <div class="col" v-for="(col,j) in row" :key="j" :style="{background:col.bg}"></div>
                        </div>
                    </div>
                    <yk-space class="level" dir="vertical" size="0" align="center">
                        <p>难度</p>
                        <yk-title :level="5">
                            <IconMinusOutline class="icon" @click="levelDown" />
                            {{gameStatus.level}}
                            <IconPlusOutline class="icon" @click="levelUp" />
                        </yk-title>
                    </yk-space>
                    <yk-space class="level" dir="vertical" size="s" align="center">
                        <p>游戏音效</p>
                        <yk-switch v-model="isChecked" checked-color="#1fb4a2" unchecked-color="#fa5247"></yk-switch>
                    </yk-space>
                    <div class="switch" @click="switchStatus">
                        <!-- 暂停/开始 -->
                        <IconPlayFill v-show="gameStatus.running" class="icon" />
                        <IconPauseFill v-show="!gameStatus.running" class="icon" />
                    </div>
                </yk-space>
            </yk-space>
            <div class="bottom">
                <p>{{ gameStatus.playTime }}</p>
            </div>
        </div>
        <DirectionControl @gameControl="getCode" :started="gameStatus.running"></DirectionControl>
    </div>
</template>

<script setup>
import DirectionControl from './DirectionControl.vue'
import { ref, reactive, onMounted, onUpdated, watch } from 'vue'
import { getNextBlock, originalBlock, renderBlock, clearBlock, isBlockOccupied, isOutOfBounds, calculateSite } from '../utils/control'
import { calculateScore, getSpeedByLevel } from '../utils/scole'
import { startRecording, stopRecording, resetRecording, getPlayTime } from '../utils/time'

// 游戏框架
const gameFrame = reactive({
    row: 20, // 行数
    col: 10, // 列数
    primary: [], // 主屏数据
    secondary: [], // 副屏数据
})
// 状态
const gameStatus = reactive({
    tickerID: -1, // 定时器ID
    running: false
})
let restart = ref(true) // 是否重新开始游戏

// 数据
let nowBlockDate = {}
let nextBlockDate = {}

// 初始化游戏框架
const initFrame = () => {
    // 主屏幕
    let newPrimary = []
    let newSecondary = []
    for (let i = 0; i < gameFrame.row; i++) {
        newPrimary.push([])
        for (let j = 0; j < gameFrame.col; j++) {
            newPrimary[i].push(originalBlock())
        }
    }

    // 副屏幕
    for (let i = 0; i < 4; i++) {
        newSecondary.push([])
        for (let j = 0; j < 4; j++) {
            newSecondary[i].push(originalBlock())
        }
    }

    gameFrame.primary = newPrimary
    gameFrame.secondary = newSecondary
}

const initStatus = () => {
    Object.assign(gameStatus, {
        over: false,   // 游戏结束
        running: false,
        score: 0,     // 分数，（1行：1 2行：3 3行：6 4行：10）* level * (1000-speed)
        level: 2,     // 等级,1-5(1:990ms 2:800ms 3:600ms 4:400ms 5:200ms)
        speed: 800,   // 速度，单位为ms
        speedChange: false, // 速度是否改变
        blockSum: 0,    // 消除的总行数，每消除10行，speed - 1ms,最小为100ms
        playTime: 0   // 游戏时长
    })
}

const initGameData = () => {
    nowBlockDate = {
        // 从data.js文件里取出来的初始数据
        site: [],
        color: '',
        transition: [],

        block: [], // 计算后的块数据
        angle: 0, // 旋转角度
        row: 0, // 在行上的位移数
        col: 3, // 在列上的位移数
    }
    nextBlockDate = {
        site: [],
        color: '',
        transition: [],
        block: [], // 计算后的块数据
        lastBlock: [], // 上一次的块数据
        angle: 0, // 旋转角度
    }
}

const renderPrimary = (opt) => {
    // console.log("renderPrimary:", opt)
    let angle = 0
    let block = []
    let oldBlock = nowBlockDate.block
    switch (opt) {
        case 'init':
            nowBlockDate.block = calculateSite(nowBlockDate.site, nowBlockDate.transition[nowBlockDate.angle], nowBlockDate.row, nowBlockDate.col)
            if (isBlockOccupied(gameFrame.primary, nowBlockDate.block)) {
                gameStatus.over = true // 游戏结束
            }
            break;
        case 'change':
            angle = nowBlockDate.angle == nowBlockDate.transition.length - 1 ? 0 : nowBlockDate.angle + 1
            block = calculateSite(nowBlockDate.site, nowBlockDate.transition[angle], nowBlockDate.row, nowBlockDate.col)
            if (isOutOfBounds(block, gameFrame.row, gameFrame.col)) {
                return
            }
            if (isBlockOccupied(gameFrame.primary, block)) {
                return
            } else {
                nowBlockDate.angle = angle
                nowBlockDate.block = block
            }
            break;
        case 'left':
            block = calculateSite(nowBlockDate.site, nowBlockDate.transition[nowBlockDate.angle], nowBlockDate.row, nowBlockDate.col - 1)
            if (isOutOfBounds(block, gameFrame.row, gameFrame.col)) {
                return
            }
            if (isBlockOccupied(gameFrame.primary, block)) {
                return
            } else {
                nowBlockDate.col -= 1
                nowBlockDate.block = block
            }
            break;
        case 'right':
            block = calculateSite(nowBlockDate.site, nowBlockDate.transition[nowBlockDate.angle], nowBlockDate.row, nowBlockDate.col + 1)
            if (isOutOfBounds(block, gameFrame.row, gameFrame.col)) {
                return
            }
            if (isBlockOccupied(gameFrame.primary, block)) {
                return
            } else {
                nowBlockDate.col += 1
                nowBlockDate.block = block
            }
            break;
        case 'down':
            block = calculateSite(nowBlockDate.site, nowBlockDate.transition[nowBlockDate.angle], nowBlockDate.row + 1, nowBlockDate.col)
            if (isOutOfBounds(block, gameFrame.row, gameFrame.col) || isBlockOccupied(gameFrame.primary, block)) {
                renderNextBlock()
                return
            } else {
                nowBlockDate.row += 1
                nowBlockDate.block = block
            }
            break;
        default:
    }
    clearBlock(gameFrame.primary, oldBlock)
    renderBlock(gameFrame.primary, nowBlockDate.block, nowBlockDate.color)
}

const renderSecondary = () => {
    clearBlock(gameFrame.secondary, nextBlockDate.lastBlock)
    renderBlock(gameFrame.secondary, nextBlockDate.block, nextBlockDate.color)
}

// 方块的消除与下移
const blockClear = () => {
    let fullRow = [] // 堆满了需要消除的行号
    let nullRow = 0  // 从下往上第一个空行
    for (let i = gameFrame.row - 1; i >= 0; i--) {
        let sum = 0
        for (let j = 0; j < gameFrame.col; j++) {
            if (gameFrame.primary[i][j].data == 1) {
                sum++
            }
        }
        if (sum == gameFrame.col) {
            // console.log("fullRow:", i)
            fullRow.push(i)
        }
        if (sum == 0) {
            nullRow = i
            // console.log("nullRow:", nullRow)
            break
        }
    }
    if (fullRow.length == 0) return
    stopGame()
    for (let i = 0; i < fullRow.length; i++) {
        for (let j = 0; j < gameFrame.col; j++) {
            gameFrame.primary[fullRow[i]][j] = originalBlock()
        }
    }

    // 消除后，把上面的方块下移
    for (let i = fullRow.length - 1; i >= 0; i--) {
        for (let j = 0; j < gameFrame.col; j++) {
            for (let clearRow = fullRow[i]; clearRow > nullRow; clearRow--) {
                // console.log("clearRow:", clearRow, "j1:", j, "i:", i)
                gameFrame.primary[clearRow][j] = gameFrame.primary[clearRow - 1][j]
                gameFrame.primary[clearRow - 1][j] = originalBlock()
            }
        }
    }
    // 计算得分
    gameStatus.score += calculateScore(fullRow.length, gameStatus.level, gameStatus.speed)
    gameStatus.blockSum += fullRow.length
    startGame()
}

// 渲染方块初始位置
const initNextBlock = () => {
    blockClear()
    Object.assign(nowBlockDate, nextBlockDate)
    nextBlockDate = getNextBlock(nextBlockDate.block)
    renderPrimary('init')
    renderSecondary()
}

const renderNextBlock = () => {
    nowBlockDate.row = 0
    nowBlockDate.col = 3
    // 记录本次方块最终位置
    for (let i = 0; i < nowBlockDate.block.length; i += 2) {
        gameFrame.primary[nowBlockDate.block[i]][nowBlockDate.block[i + 1]] = { data: 1, bg: nowBlockDate.color }
    }
    initNextBlock()
}

const restartGame = () => {
    if (gameStatus.tickerID != -1) {
        stopGame()
    }
    // 初始化
    initFrame()
    initStatus()
    initGameData()

    nextBlockDate = getNextBlock(nextBlockDate.block)
    renderSecondary()
    restart.value = true
    // 计时归零
    resetRecording()
}

watch(() => gameStatus.over, (newValue, oldValue) => {
    console.log("over:", newValue)
    if (newValue) {
        alert("游戏结束")
    }
})

onMounted(() => {
    restartGame()
    setInterval(() => {
        gameStatus.playTime = getPlayTime()
    }, 1000)
})

// 移动定时器
const setTicker = () => {
    return setInterval(() => {
        if (gameStatus.speedChange) {
            clearInterval(gameStatus.tickerID)
            gameStatus.tickerID = setTicker()
            gameStatus.speedChange = false
        }
        renderPrimary('down')
    }, gameStatus.speed)
}

const startGame = () => {
    if (restart.value) {
        initNextBlock()
        restart.value = false
    }
    gameStatus.running = true
    gameStatus.tickerID = setTicker()
    startRecording()
}

const stopGame = () => {
    gameStatus.running = false
    clearInterval(gameStatus.tickerID)
    gameStatus.tickerID = -1
    stopRecording()
}

const switchStatus = () => {
    if (gameStatus.tickerID == -1) {
        startGame()
    } else {
        stopGame()
    }
}

const levelDown = () => {
    if (gameStatus.level > 1) {
        gameStatus.level--
        gameStatus.speedChange = true
    }
}

const levelUp = () => {
    if (gameStatus.level < 5) {
        gameStatus.level++
        gameStatus.speedChange = true
    }
}



const getCode = (keycode) => {
    console.log("getCode", keycode)
    switch (keycode) {
        case 32:
            switchStatus()
            break
        case 38:
            renderPrimary('change')
            break;
        case 37:
            renderPrimary('left')
            break;
        case 39:
            renderPrimary('right')
            break;
        case 40:
            renderPrimary('down')
            break;
        default:
    }
}

watch(() => gameStatus.level, (newValue, oldValue) => {
    gameStatus.speed = getSpeedByLevel(newValue)
})


</script>

<style lang="less" scoped>
@import "@yike-design/ui/es/components/styles/basis.less";

.tetris {
    .frame {
        background: @pcolor-2;
        padding-bottom: 20px;
        border-radius: 30px;
        margin-bottom: 20px;
        .row {
            .col {
                // border-radius: 3px;
            }
        }
        .top {
            padding: 20px;
        }
        .side {
            width: 94px;
            padding: 15px;
        }
        .frame-primary {
            // padding: 1px;
            background: @bg-color-l;
            border: 2px solid @line-color-l;
            border-radius: 4px 8px;
            .row {
                display: flex;
                // border-top: 1px solid @line-color-s;
                // border-bottom: 1px solid @line-color-s;
                // margin-top: 1px;
                .col {
                    // margin-left: 1px;
                    // width: 20px;
                    // height: 20px;
                    border: 0.5px solid @line-color-s;
                    // border-right: 1px solid @line-color-s;
                    // margin: -0.5px;
                    .square {
                        width: 19px;
                        height: 19px;
                        border-radius: 3px;
                    }
                }
            }
        }
        .frame-secondary {
            background: @bg-color-l;
            // margin: 15px;
            .row {
                display: flex;
                border-top: 1px solid @bg-color-l;
                .col {
                    width: 16px;
                    height: 16px;
                    border-left: 1px solid @bg-color-l;
                }
            }
        }
        .switch {
            width: 50px;
            height: 50px;
            background: @bg-color-l;
            border-radius: 20%;
            display: grid;
            place-items: center; /* 同时水平和竖直居中 */
            cursor: pointer;
            .icon {
                color: @pcolor-6;
                font-size: 30px !important;
            }
        }
        .level {
            .icon {
                cursor: pointer;
            }
        }
        .lines {
            width: 64px;
            height: 30px;
            background: @bg-color-l;
            border-radius: 10%/20%;
            line-height: 30px;
            text-align: center;
            color: @pcolor-6;
            font-size: 16px;
        }
        .bottom {
            height: 40px;
            line-height: 40px;
            display: grid;
            place-items: center; /* 同时水平和竖直居中 */
        }
    }
}
</style>
